#pragma once 

#include <memory>
#include <string_view>
#include "flv_protocol.hpp"
#include "spdlog/spdlog.h"

namespace mms{

struct TAGDATA{
  virtual ~TAGDATA() {}

  virtual int32_t decode(const uint8_t* data, size_t len) = 0;
  virtual int32_t encode(uint8_t* data, size_t len) = 0;

  const std::string_view& get_payload() const{
        return payload;
  }  

  std::string_view payload;
};

struct AudioTagHeader {
    enum SoundFormat : uint8_t
    {
        LinearPCM_PE = 0,    // 0 = Linear PCM, platform endian
        ADPCM = 1,           // 1 = ADPCM
        MP3 = 2,             // 2 = MP3
        LinearPCM_LE = 3,    // 3 = Linear PCM, little endian
        Nellymoser16kHz = 4, // 4 = Nellymoser 16 kHz mono
        Nellymoser8kHz = 5,  // 5 = Nellymoser 8 kHz mono
        Nellymoser = 6,      // 6 = Nellymoser
        G711ALawPCM = 7,     // 7 = G.711 A-law logarithmic PCM
        G711MuLawPCM = 8,    // 8 = G.711 mu-law logarithmic PCM
        Reserved = 9,        // 9 = reserved
        AAC = 10,            // 10 = AAC
        Speex = 11,          // 11 = Speex
        MP38kHZ = 12,        // 14 = MP3 8 kHz
        DeviceSpecific = 13, // 15 = Device-specific sound
    };

    enum SoundRate : uint8_t
    {
        KHZ_5P5 = 0,
        KHZ_11 = 1,
        KHZ_22 = 2,
        KHZ_44 = 3,
    };

    enum SoundSize : uint8_t
    {
        Sample_8bit = 0,
        Sample_16bit = 1,
    };

    enum SoundType : uint8_t
    {
        MonoSound = 0,
        StereoSound = 1,
    };

    enum AACPacketType : uint8_t
    {
        AACSequenceHeader = 0,
        AACRaw = 1,
    };

public:
    enum SoundFormat sound_format : 4;
    SoundRate sound_rate : 2 = KHZ_44;
    SoundSize sound_size : 1 = Sample_16bit;
    SoundType sound_type : 1 = StereoSound;
    AACPacketType aac_packet_type = AACSequenceHeader; // IF SoundFormat == 10
public:
    bool is_seq_header()
    {
        return aac_packet_type == AACSequenceHeader;
    }

public:
    int32_t decode(const uint8_t *data, size_t len);
    int32_t encode(uint8_t *data, size_t len);
};

struct AUDIODATA : public TAGDATA
{
    AudioTagHeader header;

public:
    int32_t decode(const uint8_t *data, size_t len);
    int32_t encode(uint8_t *data, size_t len);
};

struct VideoTagHeader
{
    enum FrameType : uint8_t
    {
        KeyFrame = 1,
        InterFrame = 2,
        DisposableFrame = 3,
        GereratedKeyFrame = 4,
        VideoInfoFrame = 5,
    };

    enum CodecID : uint32_t
    {
        SorensonH264 = 2,
        ScreenVideo = 3,
        On2VP6 = 4,
        On2VP6_2 = 5,
        ScreenVideo_2 = 6,
        AVC = 7,
        HEVC = 12,
        AV1 = 13,
    };

    enum AVCPacketType : uint8_t
    {
        AVCSequenceHeader = 0,
        AVCNALU = 1,
        AVCEofSequence = 2,
    };

    enum ExtPacketType : uint8_t
    {
        PacketTypeSequeneStart = 0,
        PacketTypeCodedFrames = 1,
        PacketTypeSequenceEnd = 2,
        PacketTypeCodedFramesX = 3,
        PacketTypeMetadata = 4,
        PacketTypeMPEG2TSSequenceStart = 5,
    };

public:
    bool is_key_frame()
    {
        return frame_type == KeyFrame;
    }

    CodecID get_codec_id()
    {
        return codec_id;
    }

    bool is_seq_header();
    int32_t decode(const uint8_t *data, size_t len);
    int32_t encode(uint8_t *data, size_t len);

public:
    FrameType frame_type;
    CodecID codec_id = AVC;

    AVCPacketType avc_packet_type = AVCSequenceHeader;
    uint32_t composition_time = 0;

    int32_t size = -1; // 大小
};

struct VIDEODATA : public TAGDATA
{
    VideoTagHeader header;

public:
    uint32_t get_pts()
    {
        return pts;
    }

    uint32_t get_dts()
    {
        return dts;
    }

    void set_pts(uint32_t v)
    {
        pts = v;
    }

    void set_dts(uint32_t v)
    {
        dts = v;
    }

    uint32_t pts;
    uint32_t dts;

public:
    int32_t decode(const uint8_t *data, size_t len);
    int32_t encode(uint8_t *data, size_t len);
};

struct SCRIPTDATA : public TAGDATA
{
public:
    int32_t decode(const uint8_t *data, size_t len)
    {
        payload = std::string_view((char *)data, len);
        return len;
    }

    bool is_seq_header()
    {
        return false;
    }

    int32_t encode(uint8_t *data, size_t len)
    {
        if (len < payload.size())
        {
            return -1;
        }
        memcpy(data, payload.data(), payload.size());
        payload = std::string_view((char *)data, payload.size());
        return payload.size();
    }
};

struct FlvTag
{
public:
    FlvTagHeader tag_header;
    std::unique_ptr<TAGDATA> tag_data;
    ~FlvTag()
    {
        tag_data.reset();
    }

public:
    std::string_view get_raw_data()
    {
        return std::string_view((char *)raw_data_buf.get(), raw_data_size);
    }

    FlvTagHeader &get_tag_header()
    {
        return tag_header;
    }

    FlvTagHeader::TagType get_tag_type()
    {
        return (FlvTagHeader::TagType)(tag_header.tag_type & 0x1f);
    }

    int32_t decode(const uint8_t *data, size_t len);
    int32_t decode_and_store(const uint8_t *data, size_t len);
    int32_t encode(uint8_t *data, size_t len);

public:
    std::unique_ptr<uint8_t[]> raw_data_buf;
    size_t raw_data_size = 0;
};



}